package com.leesche.zcsp.node.net.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.mns.client.CloudAccount;
import com.aliyun.mns.client.CloudQueue;
import com.aliyun.mns.client.MNSClient;
import com.aliyun.mns.model.Message;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.iot.model.v20160530.*;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.aliyuncs.push.model.v20160801.PushRequest;
import com.aliyuncs.push.model.v20160801.PushResponse;
import com.leesche.zcsp.node.cache.JedisUtil;
import com.leesche.zcsp.node.net.service.NetDeviceService;
import com.leesche.zcsp.node.net.service.NetService;
import com.leesche.zcsp.node.net.service.data.NetDevice;
import com.leesche.zcsp.node.pay.cmbc.util.SSLClient;
import com.leesche.zcsp.node.utils.MD5Util;
import com.leesche.zcsp.node.utils.NetConfig;
import com.leesche.zcsp.node.utils.OpCode;
import com.leesche.zcsp.node.web.dao.PubDeviceInfoDao;
import com.leesche.zcsp.node.web.model.*;
import com.leesche.zcsp.node.web.service.*;
import com.leesche.zcsp.node.web.service.impl.data.read.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpResponse;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Decoder;

import java.io.*;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author reid
 * @Created 16/11/1
 */
@Service
@SuppressWarnings("All")
public class NetServiceImpl implements NetService {

    private static final Logger LOGGER = Logger.getLogger(NetServiceImpl.class);
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static final short MESSAGE_HEAD_LENTH = 23;

    private static final String INSERT_PAYDETAIL = "insert into t_pub_device_pay_detail (DeviceID, PayType, money, "
            + "Coin, Cash, Quantity, ReceiveTime, UploadTime, userid, CreateTime,recordid) " + "values (#deviceId," +
            "#payType,#money,#coin,#cash,#quantity,#receiveTime,#uploadTime,#userId,#createTime,#recordId);";

    private static final String INSERT_BALANCE = "insert into t_pub_device_balance (UserID, DeviceID,WechatBalance, "
            + "    AlipayBalance, CoinBalance, OneBalance, FileBalance, TenBalance,TwentyBalance, FiftyBalance, " +
            "" + "    HundredBalance,GameCoinOut, GameCoinBu, GameCoin,Gift, GameCoinManual, Remark, BalanceTime)" +
            "    " + "values (#userid,#deviceid,#wechatbalance,#alipaybalance,#coinbalance,#onebalance," + "    " +
            "#filebalance," + "#tenbalance,#twentybalance,#fiftyBalance,#hundredbalance,#gamecoinout," + "    " +
            "#gamecoinbu,#{gamecoin}," + "#gift,#gamecoinmanual,#remark,#balancetime);";


    @Autowired
    public PubDeviceInfoDao dao;
    @Autowired
    public PubDeviceCommandService pubDeviceCommandService;
    @Autowired
    public PubDevicePayDetailService pubDevicePayDetailService;
    @Autowired
    public PubDeviceBalanceService pubDeviceBalanceService;
    @Autowired
    public PubDeviceService pubDeviceService;
    @Autowired
    public PubDeviceInfoService pubDeviceInfoService;
    @Autowired
    public NetDeviceService netDeviceService;
    @Autowired
    private AppPushMessageService pushMessageService;
    @Autowired
    private SysUserService userService;
    @Autowired
    private ServOrderService orderService;
    @Autowired
    private PubDevSetParamService paramService;
    @Autowired
    private PubDeviceLoginService deviceLoginService;
    @Autowired
    private PubDevUpdateFirmWareService firmWareService;
    @Autowired
    private PubPlaceService pubPlaceService;
    @Autowired
    private SysParamGroupService sysParamGroupService;
    @Autowired
    private PubDevicetypeService pubDevicetypeService;
    @Autowired
    private PubParamGroupService pubParamGroupService;

    private IAcsClient client;
    private boolean isStop = false;
    private boolean isDBstop = false;

    private Thread thread;
    private Thread dbThread;

    private ConcurrentLinkedQueue<List<String>> dbqueue = new ConcurrentLinkedQueue<List<String>>();

    private ExecutorService executor = Executors.newFixedThreadPool(50);


    /**
     * @param int src 输入整数
     * @param int offset 按位偏移第几位
     * @param int length 读取的长度
     * @return byte 按位移的返回的字节
     * @Description: 一个整数按位移读取的字节
     * @author 陈辉洪
     */
    public static byte writeByte(int src, int offset, int length) {
        byte b = (byte) src;
        byte c = (byte) (b << (8 - length));
        return (byte) ((c >> offset) | 0);
    }

    /**
     * @param byte[] b
     * @return 返回二进制输出字符串
     * @Description: 输出二进制数据
     * @author 陈辉洪
     */
    public static String bytes2HexString(byte[] b) {
        StringBuffer ret = new StringBuffer();
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            ret.append(" ").append(hex.toUpperCase());
//            ret = ret + " " + hex.toUpperCase();
        }
        return ret.toString();
    }

    /**
     * @param String 输入的原始的字符串
     * @return bytebuffer bytebuffer
     * @Description: 把字符串数据转化为bytebuffer
     * @author 陈辉洪
     */
    public static ByteBuffer decode(String s) {
        if (s == null) {
            return null;
        }
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            return decoder.decodeBufferToByteBuffer(s);// decoder.decodeBuffer(s);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 从字节流里读取设备名称
     * @param buf
     * @return
     */
    public static String getDeviceName(ByteBuffer buf) {
        ByteBuffer list = ByteBuffer.allocate(21);
        int i = 0;

        while (true) {
            byte temp = buf.get();
            if (temp != 0x00) {
                list.put(temp);
            }

            i = i + 1;
            if (i >= 20) {
                break;
            }
        }

        int len = list.position();
        byte data[] = new byte[len];
        list.position(0);
        list.get(data);
        return new String(data);
    }

    public String parseJson(String json, String key) {
        JSONObject jsonObj = JSON.parseObject(json);
        String temp = jsonObj.getString("Message");

        if (temp != null) {
            JSONObject dataObj = JSON.parseObject(temp);
            return dataObj.getString(key);
        } else {
            return null;
        }
    }


    /**
     * 注册&授权设备
     * @param deviceName 设备名称
     * @param deviceType 设备类型
     * @return
     */
    @Override
    public String regDevice(String deviceName, String deviceType, boolean isNew) {
        RegistDeviceRequest request = new RegistDeviceRequest();
        request.setProductKey(NetConfig.Env_Test.productKey.getValue());
        request.setDeviceName(deviceName);//设备名称在产品内唯一,如果已存在则返回已有设备
        RegistDeviceResponse response = null;
        String server = "";
        try {
            response = client.getAcsResponse(request);
            if (!response.getSuccess()) {
                LOGGER.warn("注册设备失败,deviceName=" + deviceName + ",errorMessage:" + response.getErrorMessage());
                return "false";
            }

            //授权设备
            if (!grantDevice(deviceName, NetConfig.Env_Test.subTopic
                    .getValue() + deviceName, "SUB") || !grantDevice(deviceName, NetConfig.Env_Test.pubTopic
                    .getValue(), "PUB")) {
                return "false";
            }

            if (isNew) {
                //设备认证,获取阿里iot 服务器地址
                server = authDevice(deviceName, response.getDeviceSecret());
            }

            //插入数据库设备表
            QueryPubDeviceCondition condition = new QueryPubDeviceCondition();
            condition.setImme(deviceName);
            PubDeviceDto dto = pubDeviceService.findByCondition(condition);
            Date now = new Date();

            if (dto == null) {
                PubDevice device = new PubDevice();
                device.setDevicename(deviceName);
                device.setImme(deviceName);
                device.setDid(response.getDeviceId());
                device.setSkey(response.getDeviceSecret());
                device.setIsonline(2);//1.在线 2 离线 初始状态默认为离线
                device.setCreatetime(now);
                device.setUpdatetime(now);
                pubDeviceService.insert(device);
            } else {
                PubDevice device = pubDeviceService.selectByPrimaryKey(dto.getDeviceid());
                device.setDid(response.getDeviceId());
                device.setSkey(response.getDeviceSecret());
                device.setUpdatetime(now);
                pubDeviceService.updateByPrimaryKey(device);
            }
        } catch (Exception e) {
            LOGGER.error("注册设备失败," + e.getMessage(), e);
            return "false";
        }

        LOGGER.info("注册设备成功,deviceName=" + response.getDeviceName());

        if (isNew) {
            return response.getDeviceSecret() + "-" + response.getDeviceId() + "-" + server;
        } else {
            return response.getDeviceSecret() + "-" + response.getDeviceId();
        }

    }

    /**
     * 设备认证,获取阿里iot 服务器ip
     * @param deviceName
     * @return
     */
    @Override
    public String authDevice(String deviceName, String deviceSecret) {
        Date now = new Date();

        StringBuilder sb = new StringBuilder();
        sb.append(NetConfig.Env_Test.productSecret.getValue()).append("deviceName").append(deviceName)
          .append("productKey").append(NetConfig.Env_Test.productKey.getValue())
//                .append("resFlag").append("all")
          .append("signMethod").append("MD5").append("time").append(sdf.format(now)).append(deviceSecret);

        String sign = MD5Util.MD5Encode(sb.toString(), "utf-8");
//        LOGGER.info("sign=" + sign);

        try {
            DefaultHttpClient httpClient = new SSLClient();
            HttpPost postMethod = new HttpPost("http://iot.channel.aliyun.com/iot/auth");
            List<BasicNameValuePair> nvps = new ArrayList<BasicNameValuePair>();
            nvps.add(new BasicNameValuePair("deviceName", deviceName));
            nvps.add(new BasicNameValuePair("productKey", NetConfig.Env_Test.productKey.getValue()));
            nvps.add(new BasicNameValuePair("signMethod", "MD5"));
            nvps.add(new BasicNameValuePair("time", sdf.format(now)));
            nvps.add(new BasicNameValuePair("sign", sign.toUpperCase()));

            postMethod.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
            HttpResponse resp = httpClient.execute(postMethod);
            String result = EntityUtils.toString(resp.getEntity(), "UTF-8");
            LOGGER.info("阿里iot设备认证返回结果:" + result);

            JSONObject json = JSON.parseObject(result);
            boolean flag = json.getBoolean("success");
            if (flag) {
                String server = json.getString("servers");
                if (server.indexOf("|") > 0) {
                    server = server.substring(0, server.indexOf("|"));
                }
                String[] ipAndPort = server.split(":");
                return ipAndPort[0] + "-" + ipAndPort[1];
            }
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error(e.getMessage(), e);
        }
        return "";
    }

    /**
     * 授权设备
     * @param deviceName 设备名称
     * @param topic      topic 名称
     * @param type       权限类型:PUB,SUB,ALL
     * @return
     */
    public boolean grantDevice(String deviceName, String topic, String type) {
        DeviceGrantRequest request = new DeviceGrantRequest();
        // 设备所属的ProductKey
        request.setProductKey(new Long(NetConfig.Env_Test.productKey.getValue()));//productKey  productAppKey
        // 设备名称
        request.setDeviceName(deviceName);
        // 支持PUB,SUB,ALL
        request.setGrantType(type);
        // topic全名，包括topic所属ProductKey
        request.setTopicFullName(topic);
        DeviceGrantResponse response = null;
        try {
            response = client.getAcsResponse(request);

            if (response.getSuccess()) {
                LOGGER.info("授权设备成功,deviceName=" + deviceName + ",topic=" + topic);
                return true;
            }
        } catch (Exception e) {
            LOGGER.error("授权设备出错," + e.getMessage(), e);
        }
        return false;
    }


    /**
     * 查询设备是否在线,可批量查询
     * @param deviceName
     * @return
     */
    @Override
    public boolean isOnline(String deviceName) {
        boolean flag = false;

        BatchGetDeviceStateRequest request = new BatchGetDeviceStateRequest();
        request.setProductKey(NetConfig.Env_Test.productKey.getValue());
        List<String> devices = new ArrayList<String>();
        devices.add(deviceName);
        request.setDeviceNames(devices);
        try {
            BatchGetDeviceStateResponse response = client.getAcsResponse(request);
            if (response != null && response.getSuccess()) {
                List<BatchGetDeviceStateResponse.DeviceStatus> list = response.getDeviceStatusList();
                flag = list.get(0).getStatus().equalsIgnoreCase("ONLINE") ? true : false;
            }
        } catch (ClientException e) {
            LOGGER.error(e.getMessage(), e);
            flag = false;
        }

        int newStatus = flag ? 1 : 2;

        NetDevice netDevice = null;
        netDevice = (NetDevice) JedisUtil.hget("netDeviceMap", deviceName, new NetDevice());
        if (netDevice != null) {
            boolean status = netDevice.getIsOnline().intValue() == 1 ? true : false;

            if (flag != status) {
                netDevice.setIsonline(newStatus);
                PubDevice device = pubDeviceService.selectByPrimaryKey(netDevice.getDeviceId());
                device.setIsonline(newStatus);

                if (!flag) {
                    device.setDp(0);//设备离线,信号置0
                }
                pubDeviceService.updateByPrimaryKey(device);
            }
        }

        return flag;
    }

    /**
     * 批量查询设备在线状态
     * @param devices
     * @return ONLINE为在线, 其余为不在线
     */
    public Map<String, String> isOnlineBatch(List<String> devices) {
        Map<String, String> map = new HashMap<String, String>();
        BatchGetDeviceStateRequest request = new BatchGetDeviceStateRequest();
        request.setProductKey(NetConfig.Env_Test.productKey.getValue());
        request.setDeviceNames(devices);
        try {
            BatchGetDeviceStateResponse response = client.getAcsResponse(request);
            if (response != null && response.getSuccess()) {
                List<BatchGetDeviceStateResponse.DeviceStatus> list = response.getDeviceStatusList();
                for (BatchGetDeviceStateResponse.DeviceStatus deviceStatus : list) {
                    map.put(deviceStatus.getDeviceName(), deviceStatus.getStatus());
                }
            }
        } catch (ClientException e) {
            LOGGER.error(e.getMessage(), e);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return map;
    }

    @Override
    public void closeAliyun() {
        LOGGER.info("关闭服务器");
        isStop = true;
        isDBstop = true;
        thread.stop();
        dbThread.stop();
    }

    /**
     * @Description: 初始化阿里云物联网
     * @author 陈辉洪
     */
    public void initAliyun() {
        LOGGER.info("init AliYun Server");

        IClientProfile profile = DefaultProfile
                .getProfile(NetConfig.regionId, NetConfig.Env_Test.accesskey.getValue(), NetConfig.Env_Test.accessSecret
                        .getValue());
        client = new DefaultAcsClient(profile);

        dbThread = new Thread() {
            @Override
            public void run() {
                LOGGER.info("dbThread.run...........");

                while (!isDBstop) {
                    List<String> list = dbqueue.poll();
                    if (list != null) {
                        LOGGER.info("开始写入指令表......");

                        PubDeviceCommand command = new PubDeviceCommand();
                        command.setNtype(0L);
                        command.setDeviceid(Long.parseLong(list.get(0)));
                        command.setIsmo(Integer.parseInt(list.get(1)));
                        command.setCommandcontent(list.get(2));
                        command.setErrorcode(Integer.parseInt(list.get(3)));
                        command.setCreatetime(new Date());
                        command.setSendtime(Long.parseLong(list.get(4)));
                        command.setChecktime(Long.parseLong(list.get(5)));
                        command.setCommid(Integer.parseInt(list.get(6)));
                        command.setOrderno(list.get(7));
                        try {
                            pubDeviceCommandService.insert(command);
                        } catch (Exception e) {
                            LOGGER.error(e.getMessage(), e);
                            LOGGER.warn("dbThread.state=" + dbThread.getState());
                            continue;
                        }
                    }
                }
            }
        };
        dbThread.start();

        thread = new Thread() {
            @Override
            public void run() {
                LOGGER.info("准备从阿里MNS 里接收消息......");
                CloudAccount account = new CloudAccount(NetConfig.Env_Test.accesskey
                        .getValue(), NetConfig.Env_Test.accessSecret.getValue(), NetConfig.endPointInternal);
                MNSClient client = account.getMNSClient();
                CloudQueue queue = client.getQueueRef(NetConfig.Env_Test.queue.getValue());
                while (!isStop) {
                    LOGGER.info("start to pop message from mns...");
                    try {
                        Message popMsg = queue.popMessage(3);//设置为长轮询,等待时间3s
                        if (popMsg != null) {
                            LOGGER.info("messageId = " + popMsg.getMessageId());
                            Runnable handle = new HandleAliyunMsgThread(popMsg);
                            executor.execute(handle);

                            // 删除已经消费的消息
                            queue.deleteMessage(popMsg.getReceiptHandle());
                            LOGGER.info("delete message successfully,messageId=" + popMsg.getMessageId() + "\n");
                        }
                    /*else {
                        LOGGER.info("message not exist in Queue.\n");
                    }*/
                    } catch (Exception e) {
                        LOGGER.error(e.getMessage(), e);
                        continue;
                    }
                }
                client.close();
                LOGGER.info("---------- MNSClient close! -------------");
            }
        };
        thread.start();

    }

    /**
     * 下行指令: 启动or关闭设备
     * @param deviceName
     * @param deviceType
     * @param isOpen
     * @return
     */
    @Override
    public boolean sendClientStartOrClose(String deviceName, int deviceType, boolean isOpen) {
        ByteBuffer buf = ByteBuffer.allocate(30);
        int opCode = OpCode.START_CLOSE;
        buf.putShort((short) opCode);
        buf.put((byte) (isOpen == true ? 1 : 0));
        buf.put((byte) 0);//设备里的obj对象

        long deviceId = 0;
        NetDevice netDevice = null;
        netDevice = (NetDevice) JedisUtil.hget("netDeviceMap", deviceName, new NetDevice());
        if (netDevice != null && netDevice.getDeviceId() != null) {
            LOGGER.info("redis 缓存命中......");
            deviceId = netDevice.getDeviceId();
        } else {
            LOGGER.info("redis 缓存未命中,查询数据库......");
            QueryPubDeviceCondition condition = new QueryPubDeviceCondition();
            condition.setImme(deviceName);
            PubDeviceDto deviceDto = pubDeviceService.findByCondition(condition);
            deviceId = deviceDto.getDeviceid();
            JedisUtil.hset("deviceMap", String.valueOf(deviceDto.getDeviceid()), deviceDto);
            JedisUtil.hset("netDeviceMap", String.valueOf(deviceDto.getImme()), new NetDevice(deviceDto
                    .getDeviceid(), deviceDto.getAdminid(), deviceDto.getPlaceid(), deviceDto.getIsonline()));
        }

        Date now = new Date();
        PubDeviceInfo info = new PubDeviceInfo();
        info.setDeviceid(deviceId);
        info.setInfotype(isOpen == true ? 1 : 2);
        info.setInfocontent(isOpen == true ? "启动设备" : "关闭设备");
        info.setOccurltime(now);
        info.setReceivetime(now);
        info.setCreatetime(now);
        pubDeviceInfoService.insert(info);
        return sendClientMsg(deviceName, buf, opCode, -1, "");
    }

//    public void sendClientRepeatBuy(String deviceName, int deviceType,
//                                    int money, int coin, int payType) {
//        repeatSendService.addSynDevice(deviceName, deviceType, money, coin, payType);
//    }

    /**
     * 下行指令:服务器向设备发送请求同步交易记录指令
     * @param deviceName
     * @param deviceType
     * @param isForce
     * @return
     */
    @Override
    public boolean sendClientSyncTradeInfo(String deviceName, int deviceType, boolean isForce) {
        ByteBuffer buf = ByteBuffer.allocate(50);
        int opcode = OpCode.SYNC_TRADE_INFO;
        buf.putShort((short) opcode);
        buf.put((byte) (isForce == true ? 1 : 0));
        return sendClientMsg(deviceName, buf, opcode, -1, "");
    }

    /**
     * 设备参数设置指令
     * @param deviceName
     * @param paramType
     * @param paramValue
     * @return
     */
    @Override
    public boolean sendClientSetParam(String deviceName, int paramType, int paramValue) {
        ByteBuffer buffer = ByteBuffer.allocate(50);
        int opcode = OpCode.SET_DEVICE_PARAM;
        buffer.putShort((short) opcode);
        buffer.putShort((short) paramType);
        buffer.putShort((short) paramValue);
        return sendClientMsg(deviceName, buffer, opcode, -1, "");
    }

    /**
     * 设备参数读取指令
     * @param deviceName
     * @param paramType
     * @return
     */
    @Override
    public boolean sendClientGetParam(String deviceName, int paramType) {
        ByteBuffer buffer = ByteBuffer.allocate(50);
        int opcode = OpCode.GET_DEVICE_PARAM;
        buffer.putShort((short) opcode);
        buffer.putShort((short) paramType);
        return sendClientMsg(deviceName, buffer, opcode, -1, "");
    }

    /**
     * 查询文件接收结果
     * @param deviceName
     * @param type
     * @param version
     */
    @Override
    public boolean sendClientFileReceiveStatus(String deviceName, int type, int bigVersion, int smallVersion) {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.putShort((short) OpCode.FILE_RECEIVE_RESULT);
        buffer.put((byte) type);
        buffer.put((byte) bigVersion);
        buffer.put((byte) smallVersion);
        return sendClientMsg(deviceName, buffer, OpCode.FILE_RECEIVE_RESULT, -1, "");
    }

    /**
     * 查询设备固件版本
     * @param deviceName
     * @param type
     */
    @Override
    public boolean sendClientGetVersion(String deviceName, int type) {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.putShort((short) OpCode.QUERY_VERSION);
        buffer.put((byte) type);
        return sendClientMsg(deviceName, buffer, OpCode.QUERY_VERSION, -1, "");
    }

    /**
     * 下发立即升级
     * @param deviceName
     * @param type
     * @param version
     */
    @Override
    public boolean sendClientUpdateNow(String deviceName, int type, int bigVersion, int smallVersion) {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.putShort((short) OpCode.EXEC_UPDATE);
        buffer.put((byte) type);
        buffer.put((byte) bigVersion);
        buffer.put((byte) smallVersion);
        return sendClientMsg(deviceName, buffer, OpCode.EXEC_UPDATE, -1, "");
    }

    /**
     * 下发立即升级指令
     * @param deviceName
     * @param type
     * @param version
     */
    @Override
    public boolean sendClientUpdateInfo(String deviceName, int type, int bigVersion, int smallVersion, int option,
                                        int isBreak, String url) {
        ByteBuffer buffer = ByteBuffer.allocate(100);
        buffer.putShort((short) OpCode.UPDATE_INFO);
        buffer.put((byte) type);
        buffer.put((byte) bigVersion);
        buffer.put((byte) smallVersion);
        buffer.put((byte) option);
        buffer.put((byte) isBreak);
        try {
            buffer.put(url.getBytes("utf-8"));
            return sendClientMsg(deviceName, buffer, OpCode.UPDATE_INFO, -1, "");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 推送消息给android app
     * @param message
     * @return
     */
    @Override
    public String pushMessageToAndroid(AppPushMessage message) {
        PushRequest pushRequest = new PushRequest();
        pushRequest.setAppKey(Long.valueOf(NetConfig.androidAppKey));
        pushRequest.setPushType(message.getPushType());
        pushRequest.setDeviceType(message.getDeviceType());
        pushRequest.setTarget(message.getTarget());
        pushRequest.setTargetValue(message.getTargetValue());
        pushRequest.setTitle(message.getTitle());
        pushRequest.setBody(message.getBody());

        //android
        pushRequest.setAndroidOpenType(message.getAndroidOpenType());
        if (message.getAndroidOpenType().equalsIgnoreCase("ACTIVITY")) {
            pushRequest.setAndroidActivity(message.getAndroidOpenTarget());
        } else if (message.getAndroidOpenType().equalsIgnoreCase("URL")) {
            pushRequest.setAndroidOpenUrl(message.getAndroidOpenTarget());
        }

        //ios
        pushRequest.setiOSRemind(true);
        pushRequest.setiOSApnsEnv("PRODUCT");
        pushRequest.setiOSRemindBody(message.getBody());

        pushRequest.setStoreOffline(true); //离线消息是否保存

        try {
            PushResponse pushResponse = client.getAcsResponse(pushRequest);
            message.setPushTime(new Date());
            message.setMessageId(pushResponse.getMessageId() + "");
            pushMessageService.insert(message);
            return pushResponse.getMessageId();
        } catch (ClientException e) {
            LOGGER.error(e.getMessage(), e);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return null;
    }

/*
    @Override
    public boolean sendClientBuy(String deviceName, int deviceType, int money, int coin, int payType, String orderNo) {
        long checkTime = -1;

        //在线支付,取订单创建的时间
        if (!StringUtils.isEmpty(orderNo)) {
            ServOrder order = orderService.findServOrderByOrderNo(orderNo);
            checkTime = order.getCreatetime().getTime();
        }

        ByteBuffer buf = ByteBuffer.allocate(50);
        int opcode = OpCode.BUYREQ;
        buf.putShort((short) opcode);

        buf.put((byte) payType);//支付类型
        buf.putShort((short) money);//金额
        buf.putShort((short) coin);//出币数量
        buf.put((byte) 0);//出币口,obj,默认0

        return sendClientMsg(deviceName, buf, opcode, checkTime, orderNo);
    }
*/

    /**
     * 服务器向设备下发交易记录
     * @param PubDevice device
     * @param int       money
     * @param int       coin
     * @param int       payType 支付类型
     * @param ServOrder order 订单号
     * @Description: 发送购买
     */
    @Override
    public boolean sendClientBuy(PubDevice device, int money, int coin, int payType, ServOrder order) {
        long checkTime = -1;
        String orderNo = "";

        //在线支付,取订单创建的时间
        if (order != null) {
            checkTime = order.getCreatetime().getTime();
            orderNo = order.getOutTradeNo();
        } else {
            checkTime = System.currentTimeMillis();
        }

        ByteBuffer buf = ByteBuffer.allocate(50);
        int opcode = OpCode.BUYREQ;
        buf.putShort((short) opcode);

        buf.put((byte) payType);//支付类型
        buf.putShort((short) money);//金额
        buf.putShort((short) coin);//出币数量
        buf.put((byte) 0);//出币口,obj,默认0

        boolean flag = sendClientMsg(device.getImme(), buf, opcode, checkTime, orderNo);

        //远程补币,发送指令成功后,马上写入流水明细表
        if (flag && (payType == NetConfig.PAYTYPE_GAMECOINBU || payType == NetConfig.PAYTYPE_MEMBER_COIN)) {
            NetDevice netDevice = new NetDevice(device.getDeviceid(), device.getAdminid(), device.getPlaceid(), device
                    .getIsonline());
            insertPayDetail(netDevice, coin, payType, new Date(), checkTime);
        }

        return flag;
    }

    /**
     * 写入流水明细表
     * @param pubDevice
     * @param servOrder
     * @param payType
     */
    private void insertPayDetail(NetDevice netDevice, int count, int payType, Date uploadTime, long checkTime) {
        Date now = new Date();
        PubDevicePayDetail detail = new PubDevicePayDetail();
        detail.setDeviceid(netDevice.getDeviceId());
        detail.setPlaceid(netDevice.getPlaceId());
        detail.setUserid(netDevice.getUserId());
        detail.setPaytype(payType);
        detail.setCoin((int) count);
        detail.setCash(0);
        detail.setMoney(0);
        detail.setCreatetime(now);
        detail.setReceivetime(now);
        detail.setUploadtime(uploadTime);
        detail.setRecordid(checkTime);//checkTime

        if (payType == NetConfig.PAYTYPE_GAMECOINBU) {
            detail.setQuantity(1);//未确认
        } else {
            detail.setQuantity(0);//已确认
        }


        try {
            pubDevicePayDetailService.insert(detail);
        } catch (Exception e) {
            LOGGER.error("写入交易明细数据失败,保存SQL到临时文件! ", e);

            //写数据库失败,生成SQL 保存到临时文件
            String sql = INSERT_PAYDETAIL.replace("#deviceId", String.valueOf(detail.getDeviceid()))
                                         .replace("#payType", String.valueOf(detail.getPaytype()))
                                         .replace("#money", String.valueOf(detail.getMoney()))
                                         .replace("#coin", String.valueOf(detail.getCoin()))
                                         .replace("#cash", String.valueOf(detail.getCash()))
                                         .replace("#quantity", String.valueOf(detail.getQuantity()))
                                         .replace("#receiveTime", "'" + sdf.format(detail.getReceivetime()) + "'")
                                         .replace("#uploadTime", "'" + sdf.format(detail.getUploadtime()) + "'")
                                         .replace("#userId", String.valueOf(detail.getUserid()))
                                         .replace("#createTime", "'" + sdf.format(detail.getCreatetime()) + "'")
                                         .replace("#recordId", String.valueOf(detail.getRecordid()));
            LOGGER.info("INSERT_PAYDETAIL: " + sql);

            File file = new File("/data/backup/pay_detail.txt");
            try {
                if (!file.exists()) {
                    file.createNewFile();
                }

                FileOutputStream os = new FileOutputStream(file, true); //如果追加方式用true
                os.write((sql + "\n\n").getBytes("UTF-8"));
                os.close();
            } catch (FileNotFoundException e1) {
                LOGGER.error(e1.getMessage(), e1);
            } catch (IOException e2) {
                LOGGER.error(e2.getMessage(), e2);
            }
        }
    }

    /**
     * 下行指令:获取设备信息
     * @param deviceName
     * @param deviceType
     */
    @Override
    public boolean sendClientGetDeviceInfo(String deviceName, int deviceType) {
        ByteBuffer buffer = ByteBuffer.allocate(30);
        int opCode = OpCode.GET_DEVICE_INFO;
        buffer.putShort((short) opCode);
        buffer.put((byte) 0);//指令内容:ops=0
        return sendClientMsg(deviceName, buffer, opCode, -1, "");
    }

    /**
     * 下行指令:校对时间
     * @param deviceName
     * @param deviceType
     * @return
     */
    @Override
    public boolean sendClientSyncTime(String deviceName, int deviceType) {
        ByteBuffer buffer = ByteBuffer.allocate(30);
        int opCode = OpCode.SYNC_TIME;
        buffer.putShort((short) opCode);

        Long time = System.currentTimeMillis();
        writeTimeToBuffer(buffer, time);
        return sendClientMsg(deviceName, buffer, opCode, -1, "");
    }


    private void recordInfo(Map map) {
        List<String> list = new ArrayList<String>();
        list.add(map.get("deviceId").toString());
        list.add(map.get("isMo").toString());
        list.add(map.get("content").toString());
        list.add(map.get("errorCode").toString());
        list.add(map.get("sendTime").toString());
        list.add(map.get("checkTime").toString());
        list.add(map.get("commId").toString());
        list.add(map.get("orderNo") == null ? "" : map.get("orderNo").toString());
        dbqueue.add(list);
        LOGGER.info("dbqueue.add success......");
    }

    public void rspStartOrClose(ByteBuffer decryptBuf, String deviceId, int opcode, byte deviceType) {
        int errorCode = decryptBuf.get() & 0xff;
        int isOpen = decryptBuf.get() & 0xff;

        Date now = new Date();

        PubDeviceInfo pubDeviceInfo = new PubDeviceInfo();
        pubDeviceInfo.setInfocontent(isOpen == 1 ? "启动 " : "关闭");
        pubDeviceInfo.setInfotype(isOpen == 1 ? 1 : 2);
        pubDeviceInfo.setOccurltime(now);
        pubDeviceInfo.setReceivetime(now);
        pubDeviceInfoService.insert(pubDeviceInfo);
    }

    /**
     * 上行指令: 上传交易记录, payType=15 为对账指令, 其余为上传单条交易记录指令
     * @param decryptBuf 指令内容
     * @param deviceName 设备名称
     * @param opcode     指令CODE
     * @param sendTime   指令发送时间
     * @param checkTime  校对时间
     * @param deviceType 设备类型
     */
    public void rspPoint(ByteBuffer decryptBuf, NetDevice netDevice, int opcode, long sendTime, long checkTime, byte
            deviceType) {
        Date now = new Date();
        Date uploadTime = new Date(sendTime);
        boolean isSuccess = false;

        byte payType = decryptBuf.get();
        short count = decryptBuf.getShort();

        if (payType != NetConfig.PAYTYPE_WECHAT && payType != NetConfig.PAYTYPE_ALIPAY && payType != NetConfig
                .PAYTYPE_GAMECOINBU) {
            insertPayDetail(netDevice, count, payType, uploadTime, checkTime);
        }
    }

    /**
     * 设备上传所有交易汇总记录,即对账
     * @param decryptBuf
     * @param deviceName
     * @param opcode
     * @param sendTime
     * @param checkTime
     * @param deviceType
     */
    public void rspUploadBalance(ByteBuffer decryptBuf, NetDevice netDevice, int opcode, long sendTime, long
            checkTime, byte deviceType) {
        Date now = new Date();
        Date uploadTime = new Date(sendTime);
        boolean isSuccess = false;

        int coin = decryptBuf.getInt();//一元硬币
        int wechat = decryptBuf.getInt();//微信,总金额,单位分
        int aliPay = decryptBuf.getInt();//支付宝,总金额,单位分
        int one = decryptBuf.getInt();//一元纸币
        int five = decryptBuf.getInt();//五元纸币
        int ten = decryptBuf.getInt();//十元纸币
        int twenty = decryptBuf.getInt();//二元纸币
        int fifty = decryptBuf.getInt();//五十元纸币
        int hundred = decryptBuf.getInt();//一百元元纸币
        int gameCoinOut = decryptBuf.getInt();//出币
        int gameCoinBu = decryptBuf.getInt();//补币&远程吐币
        int gameCoin = decryptBuf.getInt();//入币/游戏币
        int gift = decryptBuf.getInt();//礼品
        int coupon = decryptBuf.getInt();//优惠码

        LOGGER.info("对账数据: " + coin + "|" + wechat + "|" + aliPay + "|" + one + "|" + five + "|" + ten + "|" + twenty
                + "|" + fifty + "|" + hundred + "|" + gameCoinOut + "|" + gameCoinBu + "|" + gameCoin + "|" + gift +
                "|" + coupon);

        PubDeviceBalance balance = new PubDeviceBalance();
        balance.setUserid(netDevice.getUserId());
        balance.setDeviceid(netDevice.getDeviceId());
        balance.setWechatbalance((long) wechat * 100);
        balance.setAlipaybalance((long) aliPay * 100);
        balance.setCoinbalance((long) coin);
        balance.setOnebalance((long) one);
        balance.setFilebalance((long) five);
        balance.setTenbalance((long) ten);
        balance.setTwentybalance((long) twenty);
        balance.setFiftybalance((long) fifty);
        balance.setHundredbalance((long) hundred);
        balance.setGamecoinout((long) gameCoinOut);
        balance.setGamecoinbu((long) gameCoinBu);
        balance.setGamecoin((long) gameCoin);
        balance.setGift((long) gift);
        balance.setGamecoinmanual((long) coupon);
        balance.setBalancetime(now);
        balance.setRemark("对账成功");

        try {
            //插入对账表
            pubDeviceBalanceService.insert(balance);
            isSuccess = true;
        } catch (Exception e) {
            LOGGER.error("写入对账数据失败,保存SQL到临时文件!", e);

            //写数据库失败,生成SQL 保存到临时文件
            String sql = INSERT_BALANCE.replace("#userid", String.valueOf(balance.getUserid()))
                                       .replace("#deviceid", String.valueOf(balance.getDeviceid()))
                                       .replace("#wechatbalance", String.valueOf(balance.getWechatbalance()))
                                       .replace("#alipaybalance", String.valueOf(balance.getAlipaybalance()))
                                       .replace("#coinbalance", String.valueOf(balance.getCoinbalance()))
                                       .replace("#onebalance", String.valueOf(balance.getOnebalance()))
                                       .replace("#filebalance", String.valueOf(balance.getFilebalance()))
                                       .replace("#tenbalance", String.valueOf(balance.getTenbalance()))
                                       .replace("#twentybalance", String.valueOf(balance.getTwentybalance()))
                                       .replace("#fiftyBalance", String.valueOf(balance.getFiftybalance()))
                                       .replace("#hundredbalance", String.valueOf(balance.getHundredbalance()))
                                       .replace("#gamecoinout", String.valueOf(balance.getGamecoinout()))
                                       .replace("#gamecoinbu", String.valueOf(balance.getGamecoinbu()))
                                       .replace("#{gamecoin}", String.valueOf(balance.getGamecoin()))
                                       .replace("#gift", String.valueOf(balance.getGift()))
                                       .replace("#gamecoinmanual", String.valueOf(balance.getGamecoinmanual()))
                                       .replace("#remark", "'" + balance.getRemark() + "'")
                                       .replace("#balancetime", "'" + sdf.format(now) + "'");
            LOGGER.info("INSERT_BALANCE: " + sql);

            File file = new File("/data/backup/pay_balance.txt");///tomcat

            try {
                if (!file.exists()) {
                    file.createNewFile();
                }

                FileOutputStream os = new FileOutputStream(file, true); //如果追加方式用true
                os.write((sql + "\n\n").getBytes("UTF-8"));
                os.close();
            } catch (FileNotFoundException e1) {
                LOGGER.error(e1.getMessage(), e1);
            } catch (IOException e2) {
                LOGGER.error(e2.getMessage(), e2);
            }
        }
//        responseIsUpdate(deviceName, deviceType, isSuccess, opcode, checkTime);
    }

    /**
     * 响应设备登录指令
     * @param decryptBuf
     * @param deviceName
     * @param opcode
     * @param sendTime
     * @param checkTime
     * @param deviceType
     */
    public void rspLogin(ByteBuffer decryptBuf, NetDevice netDevice, int opcode, long sendTime, long checkTime, byte
            deviceType) {
        int ops = decryptBuf.get() & 0xff;
        LOGGER.info("登录指令,ops = " + ops);

        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//        boolean isSuccess = false;
        try {
            PubDevice device = pubDeviceService.selectByPrimaryKey(netDevice.getDeviceId());
            device.setIsonline(1);
            device.setLastonlinetime(now);
            pubDeviceService.updateByPrimaryKey(device);
//            isSuccess = true;

            QueryPubDeviceLoginCondition condition = new QueryPubDeviceLoginCondition();
            condition.setStatDate(sdf.format(now));
            condition.setDeviceId(netDevice.getDeviceId());
            List<PubDeviceLoginDto> list = deviceLoginService.findListsByCondition(condition);
            if (list.size() == 0) {
                PubDeviceLogin record = new PubDeviceLogin();
                record.setStatDate(sdf.format(now));
                record.setDeviceId(netDevice.getDeviceId());
                record.setUserId(netDevice.getUserId());
                record.setPlaceId(netDevice.getPlaceId());
                record.setLoginCtn(1);
                record.setLastLoginTime(now);
                deviceLoginService.insert(record);
            } else {
                deviceLoginService.updateByCondition(condition);
            }

        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
//            isSuccess = false;
        } /*finally {
            responseIsUpdate(deviceName, deviceType, isSuccess, opcode, checkTime);
        }*/
    }

    /**
     * 响应设备上行校对时间指令
     * @param decryptBuf
     * @param deviceName
     * @param opcode
     * @param sendTime
     * @param checkTime
     * @param deviceType
     */
    public void rspSyncTime(ByteBuffer decryptBuf, String deviceName, int opcode, long sendTime, long checkTime, byte
            deviceType) {
        ByteBuffer buffer = ByteBuffer.allocate(30);
        buffer.putShort((short) 0);
        buffer.putShort((short) opcode);
        buffer.put((byte) 0);//errCode
        sendClientMsg(deviceName, buffer, opcode, checkTime, "");
    }

    /**
     * 响应设备上传网络信号强度指令
     * @param decryptBuf
     * @param deviceName
     * @param opcode
     * @param sendTime
     * @param checkTime
     * @param deviceType
     */
    public void rspUploadSignal(ByteBuffer decryptBuf, NetDevice netDevice, int opcode, long sendTime, long
            checkTime, byte deviceType) {
        int signal = decryptBuf.get() & 0xff;
        int errCode;

        try {
            PubDevice device = pubDeviceService.selectByPrimaryKey(netDevice.getDeviceId());
            device.setDp(Integer.valueOf(signal));
            pubDeviceService.netUpdateByPrimaryKey(device);
            errCode = 0;
        } catch (Exception e) {
            errCode = 1;
            LOGGER.error(e.getMessage(), e);
        }
    }

    /**
     * 响应设备上传设备信息指令
     * @param decryptBuf
     * @param deviceName
     * @param opcode
     * @param sendTime
     * @param checkTime
     * @param deviceType
     */
    public void rspUploadDeviceInfo(ByteBuffer decryptBuf, NetDevice netDevice, int opcode, long sendTime, long
            checkTime, byte deviceType) {
        boolean flag = updateDeviceInfo(decryptBuf, netDevice, opcode, sendTime, checkTime, deviceType);
    }

    /**
     * 设备上传文件接收状态
     */
    public void rspFirmwareReceiveStatus(ByteBuffer buffer, String deviceName, long sendTime) {
        int firmwareType = buffer.get() & 0xff;//固件类型
        int bigVersion = buffer.get() & 0xff;//大版本号
        int smallVersion = buffer.get() & 0xff;//小版本号
        int process = buffer.get() & 0xff;//进度0-100
        int errCode = buffer.get() & 0xff;//

        Map<String, String> map = new HashMap<String, String>();
        map.put("imei", deviceName);
        map.put("firmwareType", String.valueOf(firmwareType));
        map.put("bigVersion", String.valueOf(bigVersion));
        map.put("smallVersion", String.valueOf(smallVersion));
        map.put("process", String.valueOf(process));
        map.put("uploadErrCode", String.valueOf(errCode));
        map.put("upLoadComplishTime", sdf.format(new Date(sendTime)));

        firmWareService.updateByConditionMap(map);
    }

    /**
     * 设备固件升级结果上报
     */
    public void rspFirmWareUpdateStatus(ByteBuffer buffer, String deviceName, long sendTime) {
        int firmwareType = buffer.get() & 0xff;
        int bigVersion = buffer.get() & 0xff;
        int smallVersion = buffer.get() & 0xff;
        int errCode = buffer.get() & 0xff;

        Map<String, String> map = new HashMap<String, String>();
        map.put("imei", deviceName);
        map.put("firmwareType", String.valueOf(firmwareType));
        map.put("bigVersion", String.valueOf(bigVersion));
        map.put("smallVersion", String.valueOf(smallVersion));
        map.put("updateErrCode", String.valueOf(errCode));
        map.put("updComplishTime", sdf.format(new Date(sendTime)));

        firmWareService.updateByConditionMap(map);
    }

    /**
     * 设备上报固件版本信息
     */
    public void rspUploadFirmwareVersion(ByteBuffer buffer, String deviceName) {
        int firmwareType = buffer.get() & 0xff;
        int bigVersion = buffer.get() & 0xff;
        int smallVersion = buffer.get() & 0xff;
        String version = "V" + bigVersion + "." + smallVersion;

        Map<String, String> map = new HashMap<String, String>();
        map.put("imme", deviceName);
        if (firmwareType == 1) {//通讯板
            map.put("sData6", version);
        } else if (firmwareType == 2) {//业务板
            map.put("sData7", version);
        } else if (firmwareType == 3) {//第三方板
            map.put("sData8", version);
        }
        pubDeviceService.updateVersionByImme(map);
    }

    public void responseIsUpdate(String deviceName, int deviceType, boolean isSuccess, int opcode, long checkTime) {
        ByteBuffer buf = ByteBuffer.allocate(30);
        buf.putShort((short) 0);//回复类型的消息cmdId=0
        buf.putShort((short) opcode);

        int errCode;
        if (isSuccess) {
            errCode = 0;
        } else {
            errCode = 1;
        }
        buf.put((byte) errCode);

        byte[] content = new byte[buf.position()];
        buf.position(0);
        buf.get(content);
        LOGGER.info("send buffer content: " + bytes2HexString(content));
        sendClientMsg(deviceName, buf, opcode, checkTime, "");
    }

    /**
     * 收到设备返回的设备信息,更新数据库
     * @param decryptBuf
     * @param deviceName
     * @param opcode
     * @param sendTime
     * @param checkTime
     * @param deviceType
     */
    public boolean updateDeviceInfo(ByteBuffer decryptBuf, NetDevice netDevice, int opcode, long sendTime, long
            checkTime, byte deviceType) {
        int signal = decryptBuf.get() & 0xff;//信号强度
        int version1 = decryptBuf.get() & 0xff;//高位版本号
        int version2 = decryptBuf.get() & 0xff;//低位版本号
        String version = String.valueOf(version1) + "." + String.valueOf(version2);
        LOGGER.info("version=" + version);

        try {
            PubDevice device = pubDeviceService.selectByPrimaryKey(netDevice.getDeviceId());
            device.setDp(signal);
            pubDeviceService.netUpdateByPrimaryKey(device);

            SysUser sysUser = userService.selectByPrimaryKey(device.getAdminid());

            //推送消息给app
            AppPushMessage message = new AppPushMessage();
            long t1 = System.currentTimeMillis() - checkTime;
            double t2 = t1 / 1000.0;
            String body = "{\"messageType\":1,\"type\":2,\"body\":\"从服务器发送指令到设备,设备再返回消息到服务器,共耗时: " + t2 + "秒\"}";
            message.setPushType("MESSAGE");
            message.setDeviceType("ALL");
            message.setTarget("ACCOUNT");
            message.setTargetValue(sysUser.getUsername());
            message.setTitle("设备测速结果");
            message.setBody(body);
            message.setMessageType(1);
            message.setAndroidOpenType("NONE");
            pushMessageToAndroid(message);

            return true;
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return false;
    }

    /**
     * 处理设备上行指令
     * @param message
     */
    protected void handleClientMsg(String message) {
        Date now = new Date();
        Map map = new HashMap();

        NetDevice netDevice = null;

        map.put("content", message);

        String msg = parseJson(message, "message");
        LOGGER.info("receive message=" + msg);
        String status = parseJson(message, "status");
        if (status != null) {
            String deviceName = parseJson(message, "deviceName");
            LOGGER.info("deviceName=" + deviceName + " ,status=" + status);

            int isOnline = 0;

            netDevice = (NetDevice) JedisUtil.hget("netDeviceMap", deviceName, new NetDevice());
            if (netDevice != null && netDevice.getDeviceId() != null) {
                LOGGER.info("redis 缓存命中......");
                isOnline = netDevice.getIsOnline().intValue();
            } else {
                LOGGER.info("redis 缓存未命中,查询数据库...");
                QueryPubDeviceCondition condition = new QueryPubDeviceCondition();
                condition.setImme(deviceName);
                PubDeviceDto deviceDto = pubDeviceService.findByCondition(condition);
                JedisUtil.hset("deviceMap", String.valueOf(deviceDto.getDeviceid()), deviceDto);
                JedisUtil.hset("netDeviceMap", String.valueOf(deviceDto.getImme()), new NetDevice(deviceDto
                        .getDeviceid(), deviceDto.getAdminid(), deviceDto.getPlaceid(), deviceDto.getIsonline()));

                isOnline = deviceDto.getIsonline().intValue();
            }

            // 1.在线 2 离线
            int flag = status.equalsIgnoreCase("online") ? 1 : 2;
            if (isOnline != flag) {
                //登录状态改变,修改数据库
                PubDevice device = pubDeviceService.selectByPrimaryKey(netDevice.getDeviceId());
                device.setIsonline(flag);
                if (flag == 2) {
                    device.setDp(0);//设备离线,更新信号强度为0
                }
                pubDeviceService.updateByPrimaryKey(device);
            }
            return;
        }

        ByteBuffer buffer = decode(msg);
        LOGGER.info("buffer: " + bytes2HexString(buffer.array()));

        long sendTime = getTimeFromBuffer(buffer);//指令发送时间
        long checkTime = getTimeFromBuffer(buffer);//指令校对时间

        short cmdLenth = buffer.getShort();//指令内容长度
        byte deviceType = buffer.get();
        String deviceName = getDeviceName(buffer);
        LOGGER.info("deviceName=" + deviceName);
        short opcode = buffer.getShort(); //指令编号
        LOGGER.info("opcode=" + opcode);

        int errorCode = 0;
        long deviceId = 0;

        netDevice = (NetDevice) JedisUtil.hget("netDeviceMap", deviceName, new NetDevice());
        if (netDevice != null && netDevice.getDeviceId() != null) {
            LOGGER.info("redis 缓存命中......");
            deviceId = netDevice.getDeviceId();
        } else {
            LOGGER.info("redis 缓存未命中,查询数据库......");
            QueryPubDeviceCondition condition = new QueryPubDeviceCondition();
            condition.setImme(deviceName);
            PubDeviceDto deviceDto = pubDeviceService.findByCondition(condition);
            deviceId = deviceDto.getDeviceid();
            JedisUtil.hset("deviceMap", String.valueOf(deviceDto.getDeviceid()), deviceDto);
            JedisUtil.hset("netDeviceMap", String.valueOf(deviceDto.getImme()), new NetDevice(deviceDto
                    .getDeviceid(), deviceDto.getAdminid(), deviceDto.getPlaceid(), deviceDto.getIsonline()));

            netDevice = new NetDevice(deviceDto.getDeviceid(), deviceDto.getAdminid(), deviceDto.getPlaceid(), deviceDto
                    .getIsonline());
        }

        map.put("deviceId", deviceId);
        map.put("isMo", 0);
        map.put("sendTime", sendTime);
        map.put("checkTime", checkTime);

        if (opcode == OpCode.LOGIN) {//设备登录
            rspLogin(buffer, netDevice, opcode, sendTime, checkTime, deviceType);
        } else if (opcode == OpCode.UPLOAD_TRADE_INFO) {//上传交易流水
            rspPoint(buffer, netDevice, opcode, sendTime, checkTime, deviceType);
        } else if (opcode == OpCode.UPLOAD_SIGNAL) {//上传信号强度
            rspUploadSignal(buffer, netDevice, opcode, sendTime, checkTime, deviceType);
        } else if (opcode == OpCode.SYNC_TIME) {//校对时间
            rspSyncTime(buffer, deviceName, opcode, sendTime, checkTime, deviceType);
        } else if (opcode == OpCode.UPLOAD_BALANCE) {//上传对账记录
            rspUploadBalance(buffer, netDevice, opcode, sendTime, checkTime, deviceType);
        } else if (opcode == OpCode.UPLOAD_DEVICE_PARAM) {//上传设备参数
            updateDeviceParam(buffer, netDevice, opcode, errorCode);
        } else if (opcode == OpCode.FILE_RECEIVE_STATUS) {
            rspFirmwareReceiveStatus(buffer, deviceName, sendTime);
        } else if (opcode == OpCode.UPDATE_STATUS) {
            rspFirmWareUpdateStatus(buffer, deviceName, sendTime);
        } else if (opcode == OpCode.UPLOAD_VERSION) {
            rspUploadFirmwareVersion(buffer, deviceName);
        } else if (opcode == 0x00) {
            opcode = buffer.getShort(); // 命令编号
            LOGGER.info("opcode=" + opcode);
            errorCode = buffer.get() & 0xff;
            if (opcode == OpCode.GET_DEVICE_INFO) {
                //收到设备返回的设备信息
                if (errorCode == 0) {
                    updateDeviceInfo(buffer, netDevice, opcode, sendTime, checkTime, deviceType);
                }
            } else if (opcode == OpCode.SET_DEVICE_PARAM || opcode == OpCode.GET_DEVICE_PARAM) {
                updateDeviceParam(buffer, netDevice, opcode, errorCode);
            } else if (opcode == OpCode.BUYREQ) {
                updatePayDetail(netDevice, checkTime);
            }
        }

        map.put("commId", opcode);
        map.put("errorCode", errorCode);
        map.put("orderNo", "");
        LOGGER.info("errorCode=" + errorCode);
        recordInfo(map);
    }

    /**
     * 更新在线支付购币&远程补币流水记录的状态
     * @param netDevice
     * @param checkTime
     */
    private void updatePayDetail(NetDevice netDevice, long checkTime) {
        Map map = new HashMap();
        map.put("checkTime", checkTime);
        map.put("deviceId", netDevice.getDeviceId());
        pubDevicePayDetailService.updateByConditionMap(map);
    }

    /**
     * 更新设备参数
     */
    private void updateDeviceParam(ByteBuffer buffer, NetDevice netDevice, int opcode, int errCode) {
        short paramType = buffer.getShort();
        short paramValue = buffer.getShort();
        LOGGER.info("收到设备参数: paramType=" + paramType + ",paramValue=" + paramValue);

        QueryPubDevSetParamCondition condition = new QueryPubDevSetParamCondition();
        condition.setDeviceid(netDevice.getDeviceId());
        condition.setCommid((int) paramType);
        PubDevSetParamDto paramDto = paramService.selectByCondition(condition);

        //查询参数组
        SysParamGroup sysParamGroup = null;
        PubParamGroup pubParamGroup = null;
        if (paramType <= 500) {
            sysParamGroup = sysParamGroupService.selectByPrimaryKey(paramDto.getParamgroupid());
        } else {
            pubParamGroup = pubParamGroupService.selectByPrimaryKey(paramDto.getParamgroupid());
        }

        //查询设备和场地
        PubDevice pubDevice = pubDeviceService.selectByPrimaryKey(paramDto.getDeviceid());
        PubPlace pubPlace = pubPlaceService.selectByPrimaryKey(pubDevice.getPlaceid());
        PubDevicetype pubDevicetype = pubDevicetypeService.selectByPrimaryKey(pubDevice.getDevicetypeid());

        StringBuffer title = new StringBuffer();
        title.append(pubPlace.getAddress()).append(" ").append(pubDevice.getDevicename()).append(" ")
             .append(pubDevice.getDeviceshortcode()).append(" ").append(pubDevicetype.getDtname());

        Map<String, Object> paramMap = new HashMap<String, Object>();
        StringBuffer sb = new StringBuffer();
        sb.append("deviceid=").append(String.valueOf(netDevice.getDeviceId())).append(" and commid=")
          .append((int) paramType);
        //.append(" and adminid=").append(netDevice.getUserId())
        paramMap.put("condition", sb.toString());

        SysUser sysUser = userService.selectByPrimaryKey(netDevice.getUserId());
        AppPushMessage pushMessage = new AppPushMessage();
        pushMessage.setPushType("MESSAGE");
        pushMessage.setDeviceType("ALL");
        pushMessage.setTarget("ACCOUNT");
        pushMessage.setTargetValue(sysUser.getUsername());
        pushMessage.setTitle(title.toString());
        pushMessage.setMessageType(1);
        pushMessage.setAndroidOpenType("NONE");
        String body = "";

//        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
        Date now = new Date();
        String timeStr = sdf.format(now);
        paramMap.put("sdata1", timeStr);

        if (opcode == OpCode.SET_DEVICE_PARAM) {
            if (errCode == 0) {
                paramMap.put("devparamvalue", (int) paramValue);
            }
            paramMap.put("commstate", errCode);
            paramMap.put("source", "APP");
            paramService.updateSelectiveByCondition(paramMap);

            /*if (errCode == 0) {
                body = "{\"messageType\":\"1\",\"time\":\"" + timeStr + "\",\"body\":\""
                        + paramGroup.getParamgroupname() + " 命令编号: "+ paramDto.getCommid() + "\n"
                        + paramDto.getDevparamname() + " 参数值: " + paramValue + "\n" + "\"}";
            } else {
                body = "{\"messageType\":\"1\",\"time\":\"" + timeStr + "\",\"body\":\"设置参数失败," + paramDto
                        .getDevparamname() + ": " + paramValue + "\"}";
            }
            pushMessage.setBody(body);
            pushMessageToAndroid(pushMessage);*/
        } else if (opcode == OpCode.GET_DEVICE_PARAM) {
            paramMap.put("devparamvalue", (int) paramValue);
            paramMap.put("source", "DEV");
            paramMap.put("commstate", errCode);
            paramService.updateSelectiveByCondition(paramMap);
            /*if (errCode == 0) {
                body = "{\"messageType\":\"1\",\"time\":\"" + timeStr + "\",\"body\":\"获取参数成功," + paramDto
                        .getDevparamname() + ": " + paramValue + "\"}";
            } else {
                body = "{\"messageType\":\"1\",\"time\":\"" + timeStr + "\",\"body\":\"获取参数失败\"}";
            }
            pushMessage.setBody(body);
            pushMessageToAndroid(pushMessage);*/
        } else {
            paramMap.put("devparamvalue", (int) paramValue);
            paramMap.put("commstate", errCode);
            paramMap.put("source", "DEV");
            paramService.updateSelectiveByCondition(paramMap);

            StringBuffer sb1 = new StringBuffer();
            if (paramType > 500 && paramType < 800) {
                //错误参数代码,推送设备故障消息
                sb1.append("{\"messageType\":3,\"type\":1,\"time\":\"" + timeStr + "\",\"deviceId\":")
                        .append(pubDevice.getDeviceid())
                        .append(",\"paramId\":")
                        .append(paramDto.getDevparamsetid())
                        .append(",\"body\":\"");
                if (paramType <= 500) {
                    sb1.append(sysParamGroup.getParamgroupname());
                } else {
                    sb1.append(pubParamGroup.getParamgroupname());
                }
                sb1.append(" 命令编号: ").append(paramType).append("\n")
                        .append(paramDto.getDevparamname()).append(" 参数值: ").append(paramValue).append("\n")
                        .append("参数说明: ").append(paramDto.getRemark()).append("\"}");
                body = sb1.toString();
                /*body = "{\"messageType\":3,\"type\":1,\"time\":\"" + timeStr + "\",\"deviceId\":" + pubDevice
                        .getDeviceid() + ",\"paramId\":" + paramDto.getDevparamsetid() + ",\"body\":\"" + sysParamGroup
                        .getParamgroupname() + " 命令编号: " + paramType + "\n" + paramDto
                        .getDevparamname() + " 参数值: " + paramValue + "\n" + "参数说明: " + paramDto.getRemark() + "\"}";*/
            } else {
                sb1.append("{\"messageType\":1,\"type\":1,\"time\":\"" + timeStr + "\",\"deviceId\":")
                   .append(pubDevice.getDeviceid())
                   .append(",\"paramId\":")
                   .append(paramDto.getDevparamsetid())
                   .append(",\"body\":\"");
                if (paramType <= 500) {
                    sb1.append(sysParamGroup.getParamgroupname());
                } else {
                    sb1.append(pubParamGroup.getParamgroupname());
                }
                sb1.append(" 命令编号: ").append(paramType).append("\n")
                   .append(paramDto.getDevparamname()).append(" 参数值: ").append(paramValue).append("\n")
                   .append("参数说明: ").append(paramDto.getRemark()).append("\"}");
                body = sb1.toString();

                /*body = "{\"messageType\":1,\"type\":1,\"time\":\"" + timeStr + "\",\"deviceId\":" + pubDevice
                        .getDeviceid() + ",\"paramId\":" + paramDto.getDevparamsetid() + ",\"body\":\"" + sysParamGroup
                        .getParamgroupname() + " 命令编号: " + paramType + "\n" + paramDto
                        .getDevparamname() + " 参数值: " + paramValue + "\n" + "参数说明: " + paramDto.getRemark() + "\"}";*/
            }
//            body = "{\"messageType\":\"1\",\"time\":\"" + timeStr + "\",\"body\":\"收到设备上传参数," + paramDto
//                    .getDevparamname() + ": " + paramValue + "\"}";

            pushMessage.setBody(body);
            pushMessageToAndroid(pushMessage);
        }
    }

    /**
     * 发送下行指令给设备
     * @param deviceName 设备名称
     * @param value      指令内容
     * @param opcode     指令CODE
     * @param checkTime  指令校对时间
     * @param orderNo    支付订单号,仅在发送购币指令时需要传,其余指令传空字符串
     * @return
     */
    public boolean sendClientMsg(String deviceName, ByteBuffer value, int opcode, long checkTime, String orderNo) {
        boolean flag = false;

        LOGGER.info("deviceName=" + deviceName);
        LOGGER.info("opcode=" + opcode);

        long sendTime = 0;
        if (checkTime == -1) {
            sendTime = System.currentTimeMillis();
            checkTime = sendTime;
        } else {
            if (opcode == OpCode.SYNC_TIME) {
                sendTime = System.currentTimeMillis();
            } else {
                sendTime = checkTime;
            }
        }

        value.flip();

        ByteBuffer buffer = ByteBuffer.allocate(100);

        writeTimeToBuffer(buffer, sendTime);
        writeTimeToBuffer(buffer, checkTime);
        buffer.mark();
        int pos1 = buffer.position();
        buffer.putShort((short) 1);

        int deviceType = 1;
        buffer.put((byte) deviceType);
        writeDeviceName(buffer, deviceName);
        buffer.put(value);

        int pos2 = buffer.position();
        short length = (short) (pos2 - pos1 - 2 - MESSAGE_HEAD_LENTH);
        buffer.reset();
        buffer.putShort(length);

        byte data[] = new byte[pos2];
        buffer.position(0);
        buffer.get(data);

//        String msg1 = new BASE64Encoder().encode(data);

        Base64 base64 = new Base64();
        String message = null;
        try {//两次转码
            message = base64.encodeBase64String(base64.encodeBase64String(data).getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            LOGGER.error("base64 转码失败! ", e);
            flag = false;
        }

        LOGGER.info("--***** send to client message is: " + message);
        LOGGER.info("--***** send to client data is: " + bytes2HexString(data));

        Map map = new HashMap();
        long deviceId = 0;

        NetDevice netDevice = null;
        netDevice = (NetDevice) JedisUtil.hget("netDeviceMap", deviceName, new NetDevice());
        if (netDevice != null && netDevice.getDeviceId() != null) {
            LOGGER.info("redis 缓存命中......");
            deviceId = netDevice.getDeviceId();
        } else {
            LOGGER.info("redis 缓存未命中,查询数据库......");
            QueryPubDeviceCondition condition = new QueryPubDeviceCondition();
            condition.setImme(deviceName);
            PubDeviceDto deviceDto = pubDeviceService.findByCondition(condition);
            deviceId = deviceDto.getDeviceid();
            JedisUtil.hset("deviceMap", String.valueOf(deviceDto.getDeviceid()), deviceDto);
            JedisUtil.hset("netDeviceMap", String.valueOf(deviceDto.getImme()), new NetDevice(deviceDto
                    .getDeviceid(), deviceDto.getAdminid(), deviceDto.getPlaceid(), deviceDto.getIsonline()));
        }

        map.put("deviceId", deviceId);
        map.put("isMo", 1);
        map.put("content", message);
        map.put("errorCode", 0);
        map.put("sendTime", sendTime);
        map.put("checkTime", checkTime);
        map.put("commId", opcode);
        map.put("orderNo", orderNo);
        recordInfo(map);

        PubRequest pub = new PubRequest();
        pub.setProductKey(new Long(NetConfig.Env_Test.productKey.getValue()));
        pub.setMessageContent(message);
        pub.setTopicFullName(NetConfig.Env_Test.subTopic.getValue() + deviceName);
        pub.setQos(1);//保存离线消息
        PubResponse response = null;

        try {
            response = client.getAcsResponse(pub);
            if (response != null && response.getSuccess()) {
                System.out.println("response.getRequestId = " + response.getRequestId());
                flag = response.getSuccess();
                LOGGER.info("发送指令到阿里IOT 成功......");
            } else {
                LOGGER.warn("发送指令到阿里IOT 失败,error message: " + response.getErrorMessage());
            }
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }

        return flag;
    }

    /**
     * 把设备名称写入到字节流
     * @param decodeBuf
     * @param deviceName
     */
    public void writeDeviceName(ByteBuffer decodeBuf, String deviceName) {
        ByteBuffer buf = ByteBuffer.allocate(21);
        byte data[] = new byte[0];
        try {
            data = deviceName.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        buf.put(data);
        for (int i = 0; i < 20 - data.length; ++i) {
            buf.put((byte) 0x00);
        }

        buf.flip();
        decodeBuf.put(buf);
    }

    /**
     * 从字节流里读取时间戳
     * @param buffer
     * @return
     */
    public long getTimeFromBuffer(ByteBuffer buffer) {
        int time = buffer.getInt();
        int millisecond = buffer.getInt();
//        LOGGER.info("time=" + time);
//        LOGGER.info("millisecond=" + millisecond);
        return (long) time * 1000 + (long) millisecond;
    }

    /**
     * 把时间戳写入字节流
     * @param buffer
     * @param timeMillis
     */
    public void writeTimeToBuffer(ByteBuffer buffer, long timeMillis) {
        int time = (int) (timeMillis / 1000);
        int millisecond = (int) (timeMillis % 1000);
        buffer.putInt(time);
        buffer.putInt(millisecond);
    }

    /**
     * 处理设备指令的线程类
     */
    class HandleAliyunMsgThread implements Runnable {

        private Message message;

        public HandleAliyunMsgThread(Message message) {
            this.message = message;
        }

        @Override
        public void run() {
            processMessage();
        }

        public void processMessage() {
            String json = message.getMessageBodyAsBase64();
            LOGGER.info("message base64 body: " + json);
            handleClientMsg(json);
        }
    }

}
